package com.hulytu.android.hijack;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author Chris (hulytu@163.com)
 * @date 2021/11/23
 */
public final class Hijack implements InvocationHandler {

    private final String signature;
    private String appPkgName;
    private Object base;


    public Hijack(String signature) {
        this.signature = signature;
    }

    @SuppressLint({"PrivateApi", "DiscouragedPrivateApi"})
    public void hook(Context context) {
        appPkgName = context.getPackageName();

        try {
            Class<?> cls = Class.forName("android.app.ActivityThread");
            Object invoke = cls.getDeclaredMethod("currentActivityThread", new Class[0]).invoke(null);

            Field declaredField = cls.getDeclaredField("sPackageManager");
            declaredField.setAccessible(true);
            this.base = declaredField.get(invoke);

            Class<?> cls2 = Class.forName("android.content.pm.IPackageManager");

            Object newProxyInstance = Proxy.newProxyInstance(cls2.getClassLoader(), new Class[]{cls2}, this);
            declaredField.set(invoke, newProxyInstance);
            PackageManager packageManager = context.getPackageManager();
            Field declaredField2 = packageManager.getClass().getDeclaredField("mPM");
            declaredField2.setAccessible(true);
            declaredField2.set(packageManager, newProxyInstance);
        } catch (Exception ignored) {

        }
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        if ("getPackageInfo".equals(method.getName())) {

            String str = (String) args[0];

            if (((int) args[1] & PackageManager.GET_SIGNATURES) != 0 && this.appPkgName.equals(str)) {
                PackageInfo packageInfo = (PackageInfo) method.invoke(this.base, args);
                if (packageInfo != null) {
                    packageInfo.signatures = new Signature[1];
                    for (int i = 0; i < packageInfo.signatures.length; i++) {
                        packageInfo.signatures[i] = new Signature(signature);
                    }

                    return packageInfo;
                }
            }
        }

        return method.invoke(this.base, args);
    }
}
